# vim:ts=4:sw=4:noet:
""" Bytecode disassembler """

from codegen import Bytecodes
from cmg import binmsgs

extraBytes = [
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 0 push instance
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 1 push temporary
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, # 2 push literal
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 3 push constant
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # 4 pushb
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # 5 prim
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 6 pop instance/temporary
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 7 store instance
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 8 store temporary
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2, # A send
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, # B send0
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, # C send1
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, # 9 send2
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # D sendu
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # E sendb
	0,0,0,0,0,0,1,1,1,0,0,2,0,0,0,0, # F special
]

opNames = [ "pushi", "pusht", "pushl", "pushc", "pushb", "prim",  "popit", "stori",  
			"stort", "send",  "send0", "send1", "send2", "sendu", "sendb", "special" ]

def getJumps(bytecodes):
	jumps = []
	ptr=0
	while ptr<len(bytecodes):
		bc = bytecodes[ptr]
		low = bc & 0x0F
		high = bc >> 4
		ptr += 1
		if high == Bytecodes.DoSpecial:
			if low in range(6,9):
				x = bytecodes[ptr]
				jumps.append((x, (ptr-1, "label%d" % len(jumps))))
		ptr += extraBytes[bc]
	return jumps

def disassembleInstruction(bytecodes, literals, pt, inblock, jumps):
	ptr = pt
	bc = bytecodes[ptr]
	low = bc & 0x0F
	high = bc >> 4
	ret = "%03X: %02X " % (ptr, bc)
	ptr += 1
	extras = []
	for i in range(extraBytes[bc]):
		ebc = bytecodes[ptr]
		ptr +=1
		extras.append(ebc)
		ret += "%02X " % ebc
	ret = ret.ljust(12)
	if len(inblock)>0:
		if ptr>inblock[-1]:
			inblock.pop()
		ret += "  "*len(inblock)
	opname = opNames[high]

	if high in [Bytecodes.PushInstance, 
							Bytecodes.PopInstTemp,
							Bytecodes.PushTemporary,
							Bytecodes.AssignInstance,
							Bytecodes.AssignTemporary]:
		ret += "%s %d" % (opname, low)
	elif high in [Bytecodes.PushLiteral,
								Bytecodes.SendMessage0,
								Bytecodes.SendMessage1,
								Bytecodes.SendMessage2]:
		if low == 15:
			low = extras[0]
		ret += "%s %s" % (opname, literals[low])
	elif high == Bytecodes.SendMessage:
		if low == 15:
			low = extras[0]
		nargs = extras[-1]
		ret += "%s  %d, %s" % (opname, nargs, literals[low])
	elif high == Bytecodes.PushConstant:
		if low==13:
			low = "nil"
		elif low==14:
			low = "true"
		elif low==15:
			low = "false"
		elif low==11 or low==12:
			low = 10-low
		ret += "pushc %s" % low
	elif high == Bytecodes.SendUnary:
		unmsg = ["#isNil","#notNil"]
		ret += "sendu %s" % unmsg[low]
	elif high == Bytecodes.SendBinary:
		ret += "sendb #%s" % binmsgs[low]
	elif high == Bytecodes.PushBlock:
		ret += "pushb %d, %d" % (low, extras[0])
		inblock.append(extras[0])
	elif high == Bytecodes.DoPrimitive:
		ret += "prim %d" % extras[0]
	elif high == Bytecodes.DoSpecial:
		if low == 1:
			ret += "retsf"
		elif low == 2:
			ret += "retst"
		elif low == 3:
			ret += "retbk"
		elif low == 4:
			ret += "dup"
		elif low == 5:
			ret += "pop"
		elif low == 6:
			x = extras[0]
			if x in jumps:
				ret += "brnch %s" % (jumps[x][1])
			else:
				ret += "brnch 0x%03X" % (x)
		elif low == 7:
			x = extras[0]
			if x in jumps:
				ret += "brnct %s" % (jumps[x][1])
			else:
				ret += "brnct 0x%03X" % (x)
		elif low == 8:
			x = extras[0]
			if x in jumps:
				ret += "brncf %s" % (jumps[x][1])
			else:
				ret += "brncf 0x%03X" % (x)
		elif low == 11:
			ret += "sends %s" %literals[extras[0]]
		elif low == 12:
			ret += "pushst"
		elif low == 13:
			ret += "popst "
		else:
			ret += "!unknown special",low
	else:
		ret += "! unknown %d %d %d" % (bc, low, high)
	return ptr, inblock, ret

def disassembleMethod(bytecodes, literals):
	ptr = 0
	ret = ""
	literals = map(lambda x: x.asString(), literals)
	instCount = 0
	jdict = dict(getJumps(bytecodes))
	inblock = []
	while ptr < len(bytecodes):
		if ptr in jdict:
			ret += "%s:\n" % jdict[ptr][1]
		ptr, inblock, inststr = disassembleInstruction(bytecodes, literals, ptr, inblock, jdict)
		ret += " %s\n" % inststr
		instCount += 1
	return ret[:-1], instCount
